home *** CD-ROM | disk | FTP | other *** search
/ BMUG Revelations / BMUG Revelations.toast / Programming / Programming Languages / Harvest C / Examples / Sample2a.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-12  |  40.5 KB  |  1,625 lines  |  [TEXT/ALFA]

  1. /*
  2.     TransSkel - Transportable application skeleton
  3.     TransSkel is public domain.
  4.     Version 2.6 of 8/17/90
  5.  
  6.     This version by:
  7.             Bob Schumaker and Dan Hite
  8.             The AMIX Corporation
  9.             2345 Yale Street
  10.             Palo Alto, CA 94306
  11.  
  12.     UUCP:    {sun, uunet}!markets![bob,dan]
  13.     ARPA:    [bob,dan]@amix.com
  14.  
  15.      Originally written by:
  16.             Paul DuBois
  17.             Wisconsin Regional Primate Research Center
  18.             1220 Capital Court
  19.             Madison WI  53706  USA
  20.  
  21.     UUCP:    {allegra,ihnp4,seismo}!uwvax!uwmacc!dubois
  22.     ARPA:    dubois@unix.macc.wisc.edu
  23.             dubois@rhesus.primate.wisc.edu
  24. */
  25.  
  26. /*
  27.     The following symbol controls support for dialogs.
  28.     Changing #define to #undef disables the support.
  29. */
  30.  
  31. #include "Sample2a.h"
  32.  
  33. #pragma segment Seg33
  34.  
  35. #ifdef applec
  36. #include <Desk.h>
  37. #include <DiskInit.h>
  38. #include <CursorCtl.h>
  39. #include <Fonts.h>
  40. #include <Memory.h>
  41. #include <OSEvents.h>
  42. #include <Resources.h>
  43. #include <Script.h>
  44. #include <ToolUtils.h>
  45. #else
  46. #define curSysEnvVers    1
  47. #include <Color.h>
  48. #define zoomDocProc 8
  49. #define    DragGrayRgnProcPtr    ProcPtr
  50. #endif
  51.  
  52. /*
  53.     New(TypeName) returns handle to new object, for any TypeName.
  54.     If there is insufficient memory, the result is nil.
  55. */
  56.  
  57. #define    New(x)    (x **) NewHandle (sizeof (x))
  58.  
  59.  
  60. /*
  61.     Window and Menu handler types, constants, variables.
  62.  
  63.     whList and mhList are the lists of window and menu handlers.
  64.     whClobOnRmve and mhClobOnRmve are true if the handler disposal proc
  65.     is to be called when a handler is removed.  They are temporarily set
  66.     false when handlers are installed for windows or menus that already
  67.     have handlers - the old handler is removed WITHOUT calling the
  68.     disposal proc.
  69.  
  70.     Default lower limits on window sizing of 80 pixels both directions is
  71.     sufficient to allow text windows room to draw a grow box and scroll
  72.     bars without having the thumb and arrows overlap.  These values may
  73.     be changed if such a constraint is undesirable with SkelGrowBounds.
  74.     Default upper limits are for the Macintosh, not the Lisa, but are set
  75.     per machine in SkelInit.
  76. */
  77.  
  78. typedef struct WHandler
  79. {
  80.     WindowPtr    whWind;                        /* window/dialog to be handled    */
  81. #ifdef applec
  82.     void    (*whClobber)(void);                /* data structure disposal proc */
  83.     void    (*whMouse)(Point, long, short);    /* mouse-click handler proc        */
  84.     void    (*whKey)(char, unsigned char, short);    /* key-click handler proc */
  85.     void    (*whUpdate)(Boolean, short);    /* update handler proc            */
  86.     void    (*whActivate)(Boolean);            /* activate event handler proc    */
  87.     void    (*whClose)(void);                /* close "event" handler proc    */
  88.     void    (*whIdle)(void);                /* main loop proc                */
  89. #ifdef    supportDialogs
  90.     Boolean    (*whEvent)(short *, EventRecord *);/* event hook                */
  91.     Boolean    (*whCheck)(short);                /* hit notify                     */
  92. #endif
  93. #else
  94.     void    (*whClobber)();        /* data structure disposal proc */
  95.     void    (*whMouse)();        /* mouse-click handler proc        */
  96.     void    (*whKey)();            /* key-click handler proc        */
  97.     void    (*whUpdate)();        /* update handler proc            */
  98.     void    (*whActivate)();    /* activate event handler proc    */
  99.     void    (*whClose)();        /* close "event" handler proc    */
  100.     void    (*whIdle)();        /* main loop proc                */
  101. #ifdef    supportDialogs
  102.     Boolean    (*whEvent)();        /* event hook                    */
  103.     Boolean    (*whCheck)();        /* hit notify                    */
  104. #endif
  105. #endif
  106.     Rect        whGrow;            /* limits on window sizing            */
  107.     RgnHandle    whCursorRgn;    /* cursor region when front window    */
  108.     CursHandle    whCursor;        /* cursor to show while in region    */
  109.     long        userReference;    /* a user specified longword        */
  110.     short        whSizedMods;    /* keys held down while window was resized */
  111. #ifdef supportDialogs
  112.     short        whItemHit;        /* the last item hit in this dialog */
  113.     Boolean        whModal;        /* treat this as a modal dialog?    */
  114. #endif
  115.     Boolean        whCanGrow;        /* this window *can* be grown!!        */
  116.     Boolean        whSized;        /* true = window was resized        */
  117.     Boolean        whFrontOnly;    /* true = idle only when active        */
  118.     Boolean        whHaveCursor;    /* cursor associated with this window */
  119.     struct WHandler    **whNext;    /* next window handler                */
  120. } WHandler;
  121.  
  122. static WHandler    **whList = nil;
  123. static Boolean    whClobOnRmve = true;
  124. static Rect        growRect;
  125. static short mBarHeight;    /* menu bar height.  All window sizing
  126.                               code takes this into account */
  127.  
  128. static RgnHandle cursorRgn;        /* use this to manage the cursor */
  129. static RgnHandle screensRgn;
  130. static Boolean useWaitNextEvent;
  131. static SysEnvRec skelEnvRec;
  132.  
  133. typedef struct MHandler
  134. {
  135.     short            mhID;                        /* menu id                     */
  136. #ifdef applec
  137.     void            (*mhSelect)(short);            /* item selection handler proc */
  138.     void            (*mhClobber)(MenuHandle);    /* menu disposal handler proc  */
  139. #else
  140.     void            (*mhSelect)();            /* item selection handler proc */
  141.     void            (*mhClobber)();            /* menu disposal handler proc  */
  142. #endif
  143.     struct MHandler    **mhNext;                    /* next menu handler           */
  144. } MHandler;
  145.  
  146.  
  147. static MHandler    **mhList = nil;            /* list of menu handlers */
  148. static Boolean    mhClobOnRmve = true;
  149.  
  150.  
  151. /*
  152.     Variables for default Apple menu handler.  appleID is set to 1 if
  153.     SkelApple is called and is the id of the Apple menu, appleAboutProc
  154.     is the procedure to execute if there is an About... item and it's
  155.     chosen from the Apple menu.  If doAbout is true, then the menu
  156.     contains the About... item, otherwise it's just desk accessories.
  157. */
  158.  
  159. static MenuHandle    appleMenu;
  160. static short        appleID = 0;
  161. #ifdef applec
  162. static void            (*appleAboutProc)(void) = nil;
  163. static void            (*appleHelpProc)(void) = nil;
  164. #else
  165. static void            (*appleAboutProc)() = nil;
  166. static void            (*appleHelpProc)() = nil;
  167. #endif
  168. static Boolean        doAbout = false;
  169. static Boolean        doHelp = false;
  170. /*
  171.     Miscellaneous
  172.  
  173.     screenPort points to the window manager port.
  174.     
  175.     doneFlag determines when SkelMain returns.  It is set by calling
  176.     SkelWhoa(), which the host does to request a halt.
  177.  
  178.     pBkgnd points to a background procedure, to be run during event
  179.     processing.  Set it with SkelBackground.  If nil, there's no
  180.     procedure.
  181.  
  182.     pEvent points to an event-inspecting hook, to be run whenever an
  183.     event occurs.  Set it with SkelEventHook.  If nil, there's no
  184.     procedure.
  185.  
  186.     eventMask controls the event types requested in the GetNextEvent
  187.     call in SkelMain.
  188.  
  189.     diskInitPt is the location at which the disk initialization dialog
  190.     appears, if an uninitialized disk is inserted.
  191. */
  192.  
  193. static GrafPtr    screenPort;
  194. static Boolean    doneFlag = false;
  195.  
  196. #ifdef applec
  197. static Boolean    (*pEvent)(EventRecord *) = nil;
  198. static void        (*pBkgnd)(void) = nil;
  199. #else
  200. static Boolean    (*pEvent)() = nil;
  201. static void        (*pBkgnd)() = nil;
  202. #endif
  203. static long     pFGTime = 6L;            /* tenth of a second */
  204. static long        pBGTime = 300L;            /* 5 seconds */
  205. static Boolean    fgApp = true;            /* handle suspend/resume events */
  206.  
  207. static short    eventMask = everyEvent ^ keyUpMask;
  208. static Point    diskInitPt = { /* v = */ 120, /* h = */ 100 };
  209.  
  210. #ifdef applec
  211. static void (*pScrapConvert)(Boolean) = nil;
  212. static void (*pSuspendResume)(Boolean) = nil;
  213. static void (*pZoomProc)(WindowPtr, short, short, Boolean) = nil;
  214. static void (*pNetworkProc)(EventRecord *) = nil;
  215. static void (*pChildDied)(EventRecord *) = nil;
  216. static void (*pHandleApp4)(EventRecord *) = nil;
  217. #else
  218. static void (*pScrapConvert)() = nil;
  219. static void (*pSuspendResume)() = nil;
  220. static void (*pZoomProc)() = nil;
  221. static void (*pNetworkProc)() = nil;
  222. static void (*pChildDied)() = nil;
  223. static void (*pHandleApp4)() = nil;
  224. #endif
  225.  
  226. #ifdef    supportDialogs
  227.  
  228. /*
  229.     Events that are passed to dialogs.  Others are ignored.
  230.     Standard mask passes , mousedown, keydown, autokey, update,
  231.     activate and null events.  Null events are controlled by bit 0.
  232. */
  233.  
  234. static short    dlogEventMask = 0x16b;
  235.  
  236. #endif
  237.  
  238. /* -------------------------------------------------------------------- */
  239. /*                        Internal (private) Routines                        */
  240. /* -------------------------------------------------------------------- */
  241.  
  242. /*
  243.     Get handler associated with user or dialog window.
  244.     Return nil if window doesn't belong to any known handler.
  245.     This routine is absolutely fundamental to TransSkel.
  246. */
  247.  
  248. WHandler **GetWDHandler (WindowPtr);
  249. static WHandler **GetWDHandler (theWind)
  250. WindowPtr    theWind;
  251. {
  252.     register WHandler    **h;
  253.  
  254.     if (theWind) {
  255.         for (h = whList; h != nil; h = (**h).whNext) {
  256.             if ((**h).whWind == theWind) {
  257.                 return (h);
  258.             }
  259.         }
  260.     }
  261.     return (nil);
  262. }
  263.  
  264.  
  265. /*
  266.     Get handler associated with user window.
  267.     Return nil if window doesn't belong to any known handler.
  268.     The order of the two tests is critical:  theWind might be nil.
  269. */
  270.  
  271. WHandler **GetWHandler (WindowPtr);
  272. static WHandler **GetWHandler (theWind)
  273. WindowPtr    theWind;
  274. {
  275.     register WHandler    **h;
  276.  
  277.     if ((h = GetWDHandler (theWind)) != nil
  278.         && ((WindowPeek) theWind)->windowKind != dialogKind) {
  279.         return (h);
  280.     }
  281.     return (nil);
  282. }
  283.  
  284. #ifdef    supportDialogs
  285.  
  286. /*
  287.     Get handler associated with dialog window.
  288.     Return nil if window doesn't belong to any known handler.
  289.     The order of the two tests is critical:  theDialog might be nil.
  290. */
  291.  
  292. WHandler **GetDHandler (DialogPtr);
  293. static WHandler **GetDHandler (theDialog)
  294. DialogPtr    theDialog;
  295. {
  296.     register WHandler    **h;
  297.  
  298.     if ((h = GetWDHandler (theDialog)) != nil
  299.         && ((WindowPeek) theDialog)->windowKind == dialogKind)
  300.     {
  301.             return (h);
  302.     }
  303.     return (nil);
  304. }
  305.  
  306. #endif
  307.  
  308.   static WHandler **
  309. SkelNullHandler (theWin)
  310.     WindowPtr theWin;
  311. {
  312.     register WHandler    **h;
  313.     if (h = New (WHandler)) {
  314.         (**h).whWind = theWin;
  315.         (**h).whMouse = nil;
  316.         (**h).whKey = nil;
  317.         (**h).whUpdate = nil;
  318.         (**h).whActivate = nil;
  319.         (**h).whClose = nil;
  320.         (**h).whClobber = nil;
  321.         (**h).whIdle = nil;
  322.         (**h).whFrontOnly = true;
  323.         (**h).whSized = false;
  324.         (**h).whSizedMods = 0;
  325.         (**h).whGrow = growRect;
  326.         (**h).whCanGrow = true;
  327.         (**h).whHaveCursor = false;
  328.         (**h).whCursorRgn = (RgnHandle) 0;
  329.         (**h).whCursor = (CursHandle) 0;
  330.         (**h).userReference = 0L;
  331. #ifdef supportDialogs
  332.         (**h).whEvent = nil;
  333.         (**h).whCheck = nil;
  334.         (**h).whItemHit = 0;
  335.         (**h).whModal = false;
  336. #endif
  337.         /* add this into the window list! */
  338.         (**h).whNext = whList;
  339.         whList = h;
  340.     }
  341.     return (h);
  342. }
  343.  
  344.   void
  345. SkelDragWindow (theWin, pt, mods, bounds)
  346.     WindowPtr theWin;
  347.     Point pt;
  348.     short mods;
  349.     Rect *bounds;
  350. {
  351.     if (!(mods & cmdKey)) {
  352.         SelectWindow (theWin);
  353.         SetPort (theWin);
  354.         if (!StillDown ())
  355.             return;
  356.     }
  357.     DragWindow (theWin, pt, bounds);
  358. }
  359.  
  360. /*
  361.     Remove a menu handler.  This calls the handler's disposal routine
  362.     and then takes the handler out of the handler list and disposes
  363.     of it.
  364.  
  365.     Note that the menu MUST be deleted from the menu bar before calling
  366.     the clobber proc, because the menu bar will end up filled with
  367.     garbage if the menu was allocated with NewMenu (see discussion of
  368.     DisposeMenu in Menu Manager section of Inside Macintosh).
  369.  
  370.     The menu bar is only redrawn if redrawBar is true--this removes
  371.     the flicker from the menu bar when the application terminates.
  372. */
  373.  
  374. void RemoveMenu (MenuHandle, Boolean);
  375. static void RemoveMenu (theMenu, redrawBar)
  376.     MenuHandle    theMenu;
  377.     Boolean        redrawBar;
  378. {
  379.     register short        mID;
  380.     register MHandler    **h, **h2;
  381. #ifdef applec
  382.     register void        (*p)(MenuHandle);
  383. #else
  384.     register void        (*p)();
  385. #endif
  386.  
  387.     mID = (**theMenu).menuID;
  388.     /* if list empty, ignore */
  389.     if (mhList != nil) {
  390.         /* is it the first element? */
  391.         if ((**mhList).mhID == mID) {
  392.             h2 = mhList;
  393.             mhList = (**mhList).mhNext;
  394.         }
  395.         else {
  396.             for (h = mhList; h != nil; h = h2) {
  397.                 h2 = (**h).mhNext;
  398.                 if (h2 == nil)
  399.                     return;                        /* menu not in list! */
  400.                 if ((**h2).mhID == mID) {
  401.                     /* found it */
  402.                     (**h).mhNext = (**h2).mhNext;
  403.                     break;
  404.                 }
  405.             }
  406.         }
  407.         DeleteMenu (mID);
  408.         if (redrawBar) {
  409.             DrawMenuBar ();
  410.         }
  411.         if (mhClobOnRmve && (p = (**h2).mhClobber) != nil) {
  412.             (*p) (theMenu);                        /* call disposal routine */
  413.         }
  414.         DisposHandle ((Handle) h2);                /* get rid of handler record */
  415.     }
  416. }
  417.  
  418. /*
  419.     General menu-handler.  Just passes selection to the handler's
  420.     select routine.  If the select routine is nil, selecting items from
  421.     the menu is a nop.
  422. */
  423.  
  424. void DoMenuCommand (long);
  425. static void DoMenuCommand (command)
  426. long        command;
  427. {
  428.     register short        menu;
  429.     register short        item;
  430.     register MHandler    **mh;
  431. #ifdef applec
  432.     register void        (*p)(short);
  433. #else
  434.     register void        (*p)();
  435. #endif
  436.  
  437.     menu = HiWord (command);
  438.     item = LoWord (command);
  439.     for (mh = mhList; mh != nil; mh = (**mh).mhNext) {
  440.         if ((menu == (**mh).mhID) && ((p = (**mh).mhSelect) != nil)) {
  441.             (*p) (item);
  442.             break;
  443.         }
  444.     }
  445.     HiliteMenu (0);        /* command done, turn off menu hiliting */
  446. }
  447.  
  448.  
  449. /*
  450.     Apple menu handler
  451.     
  452.     DoAppleItem:  If the first item was chosen, and there's an "About..."
  453.     item, call the procedure associated with it (if not nil).  If there
  454.     is no "About..." item or the item was not the first one, then open
  455.     the associated desk accessory.  The port is saved and restored
  456.     because OpenDeskAcc does not always preserve it correctly.
  457.     
  458.     DoAppleClobber disposes of the Apple menu.
  459. */
  460.  
  461. void DoAppleItem (short);
  462. static void DoAppleItem (item)
  463.     short item;
  464. {
  465. GrafPtr        curPort;
  466. Str255        str;
  467.  
  468.     if (doAbout && item == 1)
  469.     {
  470.         if (appleAboutProc != nil)
  471.             (*appleAboutProc) ();
  472.     }
  473.     else if (doHelp && item == 2) {
  474.         if (appleHelpProc != nil) {
  475.             (*appleHelpProc) ();
  476.         }
  477.     }
  478.     else
  479.     {
  480.         GetPort (&curPort);
  481.         GetItem (appleMenu, item, str);        /* get DA name */
  482.         (void) OpenDeskAcc (str);            /* open it */
  483.         SetPort (curPort);
  484.     }
  485. }
  486.  
  487. void DoAppleClobber (void);
  488. static void DoAppleClobber ()
  489. {
  490.     DisposeMenu (appleMenu);
  491. }
  492.  
  493.  
  494. /* -------------------------------------------------------------------- */
  495. /*                        Window-handler routing routines                    */
  496. /*                                                                        */
  497. /*    Each routine sets the port to the handler's window before executing    */
  498. /*    the handler procedure.                                                */
  499. /* -------------------------------------------------------------------- */
  500.  
  501.  
  502. /*
  503.     Pass local mouse coordinates, click time, and the modifiers flag
  504.     word to the handler.
  505. */
  506.  
  507. void DoMouse (WHandler **, EventRecord *);
  508. static void DoMouse (h, theEvent)
  509.     WHandler    **h;
  510.     EventRecord    *theEvent;
  511. {
  512. #ifdef applec
  513.     register void    (*p)(Point, long, short);
  514. #else
  515.     register void    (*p)();
  516. #endif
  517.     Point            thePt;
  518.  
  519.     if (h != nil) {
  520.         SetPort ((**h).whWind);
  521.         if ((p = (**h).whMouse) != nil) {
  522.             thePt = theEvent->where;    /* make local copy */
  523.             GlobalToLocal (&thePt);
  524.             (*p) (thePt, theEvent->when, theEvent->modifiers);
  525.         }
  526.     }
  527. }
  528.  
  529.  
  530. /*
  531.     Pass the character and the modifiers flag word to the handler.
  532. */
  533.  
  534. void DoKey (WHandler **, char, unsigned char, short);
  535. static void DoKey (h, ch, code, mods)
  536.     WHandler        **h;
  537.     char              ch;
  538.     unsigned char      code;
  539.     short              mods;
  540. {
  541. #ifdef applec
  542.     register void (*p)(char, unsigned char, short);
  543. #else
  544.     register void (*p)();
  545. #endif
  546.     if (h != nil) {
  547.         SetPort ((**h).whWind);
  548.         if ((p = (**h).whKey) != nil)
  549.             (*p) (ch, code, mods);
  550.     }
  551. }
  552.  
  553.  
  554. /*
  555.     Call the window updating procedure, passing to it an indicator whether
  556.     the window has been resized or not.  Then clear the flag, assuming
  557.     the update proc took whatever action was necessary to respond to
  558.     resizing.
  559.  
  560.     If the handler doesn't have any update proc, the Begin/EndUpdate
  561.     stuff is still done, to clear the update region.  Otherwise the
  562.     Window Manager will keep generating update events for the window,
  563.     stalling updates of other windows.
  564.  
  565.     Make sure to save and restore the port, as it's not always the
  566.     active window that is updated.
  567. */
  568.  
  569. void DoUpdate (WHandler **);
  570. static void DoUpdate (h)
  571.     WHandler    **h;
  572. {
  573. #ifdef applec
  574.     register void        (*p)(Boolean, short);
  575. #else
  576.     register void        (*p)();
  577. #endif
  578.     register GrafPtr    updPort;
  579.     GrafPtr                tmpPort;
  580.  
  581.     if (h != nil) {
  582.         GetPort (&tmpPort);
  583.         SetPort (updPort = (**h).whWind);
  584.         BeginUpdate (updPort);
  585.         if ((p = (**h).whUpdate) != nil) {
  586.             (*p) ((**h).whSized, (**h).whSizedMods);
  587.             (**h).whSized = false;
  588.             (**h).whSizedMods = 0;
  589.         }
  590.         EndUpdate (updPort);
  591.         SetPort (tmpPort);
  592.     }
  593. }
  594.  
  595.  
  596. /*
  597.     Pass activate/deactivate notification to handler.
  598. */
  599.  
  600. static void DoActivate (theWin, active)
  601.     WindowPtr    theWin;
  602.     Boolean        active;
  603. {
  604. #ifdef applec
  605.     register void (*p)(Boolean);
  606. #else
  607.     register void (*p)();
  608. #endif
  609.     WHandler    **h;
  610.  
  611.     if ((h = GetWDHandler (theWin)) != nil) {
  612.         SetPort ((**h).whWind);
  613.         if ((p = (**h).whActivate) != nil) {
  614.             (*p) (active);
  615.         }
  616.         /* reset the cursor region */
  617.         if (active) {
  618.             SetEmptyRgn (cursorRgn);
  619.         }
  620.     }
  621. }
  622.  
  623.  
  624. /*
  625.     Execute a window handler's close proc.  This may be used by handlers
  626.     for temp windows that want to remove themselves when the window
  627.     is closed:  they can call SkelRmveWind to dispose of the window
  628.     and remove the handler from the window handler list.  Thus, windows
  629.     may be dynamically created and destroyed without filling up the
  630.     handler list with a bunch of invalid handlers.
  631.     
  632.     If the handler doesn't have a close proc, just hide the window.
  633.     The host should provide some way of reopening the window (perhaps
  634.     a menu selection).  Otherwise the window will be lost from user
  635.     control if it is hidden, since it won't receive user events.
  636.  
  637.     The port is set to the window manager port after calling the
  638.     handler proc, to avoid a dangling port.
  639.  
  640.     This is called both for regular and dialog windows.
  641. */
  642.  
  643. void DoClose (WHandler **);
  644. static void DoClose (h)
  645.     WHandler    **h;
  646. {
  647. #ifdef applec
  648.     register void        (*p)(void);
  649. #else
  650.     register void        (*p)();
  651. #endif
  652.     GrafPtr savePort;
  653.  
  654.     if (h != nil) {
  655.         if ((p = (**h).whClose) != nil) {
  656.             GetPort (&savePort);
  657.             SetPort ((**h).whWind);
  658.             (*p) ();
  659.             SetPort (savePort);
  660.         }
  661.         else {
  662.             HideWindow ((**h).whWind);
  663.         }
  664.     }
  665. }
  666.  
  667.  
  668. /*
  669.     Execute a window handler's clobber proc.
  670.  
  671.     The port is set to the window manager port after calling the
  672.     handler proc, to avoid a dangling port.
  673.  
  674.     This is called both for regular and dialog windows.
  675. */
  676.  
  677. void DoClobber (WHandler **);
  678. static void DoClobber (h)
  679.     WHandler    **h;
  680. {
  681. #ifdef applec
  682.     register void (*p)(void);
  683. #else
  684.     register void (*p)();
  685. #endif
  686.     GrafPtr    tmpPort;
  687.  
  688.     GetPort (&tmpPort);
  689.     SetPort ((**h).whWind);
  690.     if ((p = (**h).whClobber) != nil)
  691.         (*p) ();
  692.     SetPort (tmpPort);
  693. }
  694.  
  695.  
  696. /*
  697.     Execute handler's idle proc.
  698.  
  699.     Make sure to save and restore the port, since idle procs may be
  700.     called for any window, not just the active one.
  701. */
  702.  
  703. void DoIdle (WHandler **);
  704. static void DoIdle (h)
  705.     WHandler    **h;
  706. {
  707. #ifdef applec
  708.     register void (*p)(void);
  709. #else
  710.     register void (*p)();
  711. #endif
  712.     GrafPtr            tmpPort;
  713.  
  714.     if (h != nil) {
  715.         if (!useWaitNextEvent) {
  716.             SystemTask ();
  717.         }
  718.         GetPort (&tmpPort);
  719.         SetPort ((**h).whWind);
  720.         if ((p = (**h).whIdle) != nil)
  721.             (*p) ();
  722.         SetPort (tmpPort);
  723.     }
  724. }
  725.  
  726.  
  727. #ifdef    supportDialogs
  728.  
  729. /* -------------------------------------------------------------------- */
  730. /*                            Dialog-handling routines                    */
  731. /* -------------------------------------------------------------------- */
  732.  
  733.  
  734. /*
  735.     Handle event if it's for a dialog.  The event must be one of
  736.     those that is passed to dialogs according to dlogEventMask.
  737.     This mask can be set so that disk-inserts, for instance, don't
  738.     get eaten up.
  739. */
  740.  
  741. Boolean DoDialog (EventRecord *);
  742. static Boolean DoDialog (evt)
  743.     EventRecord *evt;
  744. {
  745.     register WHandler    **dh;
  746.     DialogPtr            theDialog;
  747.     register short        what;
  748.     short                item;
  749.     Boolean                handledIt = false;
  750. #ifdef applec
  751.     Boolean                (*pKeys)(short *, EventRecord *);
  752. #else
  753.     Boolean                (*pKeys)();
  754. #endif
  755.  
  756.     theDialog = (DialogPtr) FrontWindow ();
  757.     if ((dh = GetDHandler (theDialog)) != nil) {
  758. #ifdef applec
  759.         pKeys = (Boolean (*)(short *, EventRecord *)) (**dh).whKey;
  760. #else
  761.         pKeys = (Boolean (*)()) (**dh).whKey;
  762. #endif
  763.     }
  764.     else {
  765.         pKeys = nil;
  766.     }
  767.     what = evt->what;
  768. /* handle command keys before they get to IsDialogEvent */
  769.     if ((what == keyDown || what == autoKey) && (evt->modifiers & cmdKey)) {
  770.         if (!(pKeys && (handledIt = (*pKeys) (&item, evt)))) {
  771.             if (!(dh && (**dh).whModal)) {
  772.                 DoMenuCommand (MenuKey (evt->message & charCodeMask));
  773.             }
  774.             return (true);
  775.         }
  776.     }
  777.     else if (what == app4Evt || what == updateEvt) {
  778.         /* do multifinder events, updates are handled in the main loop */
  779.         if (dh && (**dh).whEvent) {
  780.             handledIt = (*(**dh).whEvent)(&item, evt);
  781.         }
  782.         return (handledIt);               /* main section handles these */
  783.     }
  784.     else if (((1 << what) & dlogEventMask) && IsDialogEvent (evt)) {
  785.         item = 0;
  786.         switch (what) {
  787.             case keyDown:
  788.             case autoKey:
  789.                 if (!(pKeys && (handledIt = (*pKeys) (&item, evt)))) {
  790.                     handledIt = DialogSelect (evt, &theDialog, &item);
  791.                 }
  792.                 break;
  793.             case activateEvt:                        /* if activate */
  794.                 theDialog = (DialogPtr) evt->message;
  795.                 dh = GetDHandler ((WindowPtr) theDialog);
  796.                 if ((evt->modifiers & activeFlag)    /* and coming active */
  797.                     && ((WindowPeek) theDialog)->windowKind == dialogKind) {
  798.                     SetPort ((GrafPtr) theDialog);
  799.                 }
  800.             /* fall through */
  801.             default:
  802.                 if (!(dh && (**dh).whEvent &&
  803.                         (handledIt = (*(**dh).whEvent)(&item, evt))    )) {
  804.                     handledIt = DialogSelect (evt, &theDialog, &item);
  805.                 }
  806.                 break;
  807.         }
  808.         if ((dh = GetDHandler ((WindowPtr) theDialog)) != nil) {
  809.             (**dh).whItemHit = item;
  810.             /* if this is a modal dialog, whCheck will be called elsewhere */
  811.             if ((**dh).whCheck && !(**dh).whModal) {
  812.                 (*(**dh).whCheck) (item);
  813.             }
  814.         }
  815.     }
  816.     return (handledIt);
  817. }
  818.  
  819. #endif
  820.  
  821. /* -------------------------------------------------------------------- */
  822. /*                            Event-handling routines                        */
  823. /* -------------------------------------------------------------------- */
  824.  
  825.  
  826. void SkelCheckCursor (WHandler **, Point, RgnHandle);
  827.   static void
  828. SkelCheckCursor (wh, mouse, region)
  829.     WHandler **wh;
  830.     Point mouse;
  831.     RgnHandle region;
  832. {
  833.     Point pt;
  834.     RgnHandle arrowRgn, localRgn;
  835.  
  836.     if (wh && (**wh).whHaveCursor) {
  837.         arrowRgn = NewRgn ();
  838.         localRgn = NewRgn ();
  839.         CopyRgn ((**wh).whCursorRgn, localRgn);
  840.         CopyRgn (screensRgn, arrowRgn);
  841.         SetPt (&pt, 0, 0);
  842.         SetPort ((**wh).whWind);
  843.         LocalToGlobal (&pt);
  844.         OffsetRgn (localRgn, pt.h, pt.v);
  845.         DiffRgn (arrowRgn, localRgn, arrowRgn);
  846.  
  847.         if (PtInRgn (mouse, localRgn)) {
  848.             SetCursor (*(**wh).whCursor);
  849.             CopyRgn (localRgn, region);
  850.         }
  851.         else {
  852.             InitCursor ();
  853.             CopyRgn (arrowRgn, region);
  854.         }
  855.         DisposeRgn (localRgn);
  856.         DisposeRgn (arrowRgn);
  857.     }
  858.     else {
  859.         InitCursor ();
  860.         CopyRgn (screensRgn, region);
  861.     }
  862. }
  863.  
  864. /*
  865.     Have either zoomed a window or sized it manually.  Invalidate
  866.     it to force an update and set the 'resized' flag in the window
  867.     handler true.  The port is assumed to be set to the port that changed
  868.     size.
  869. */
  870.  
  871. void TriggerUpdate (WHandler **, GrafPtr, short);
  872. static void TriggerUpdate (h, grownPort, mods)
  873.     WHandler    **h;
  874.     GrafPtr        grownPort;
  875.     short        mods;
  876. {
  877.     GrafPtr savePort;
  878.  
  879.     GetPort (&savePort);
  880.     SetPort (grownPort);
  881.     InvalRect (&grownPort->portRect);
  882.     if (h != nil) {
  883.         (**h).whSized = true;
  884.         (**h).whSizedMods = mods;
  885.         if ((**h).whHaveCursor && grownPort == FrontWindow ()) {
  886.             SetEmptyRgn (cursorRgn);
  887.         }
  888.     }
  889.     SetPort (savePort);
  890. }
  891.  
  892. /*
  893.     Size a window.  If the window has a handler, use the grow limits
  894.     in the handler record, otherwise use the defaults.
  895.  
  896.     The portRect is invalidated to force an update event.  (The port
  897.     must be set first, as it could be pointing anywhere.)  The handler's
  898.     update procedure should check the parameter passed to it to check
  899.     whether the window has changed size, if it needs to adjust itself to
  900.     the new size.  THIS IS A CONVENTION.  Update procs must notice grow
  901.     "events", there is no procedure specifically for such events.
  902.     
  903.     The clipping rectangle is not reset.  If the host application
  904.     keeps the clipping set equal to the portRect or something similar,
  905.     then it will have to arrange to treat window growing with more
  906.     care.
  907.  
  908.     Since the grow region of only the active window may be clicked,
  909.     it should not be necessary to set the port.
  910. */
  911.  
  912. void DoGrow (WHandler **, GrafPtr, Point, short);
  913. static void DoGrow (h, thePort, startPt, mods)
  914.     WHandler    **h;
  915.     GrafPtr        thePort;
  916.     Point        startPt;
  917.     short        mods;
  918. {
  919.     Rect                r;
  920.     register long        growRes;
  921.  
  922.     r = (**h).whGrow;
  923.     /* grow result non-zero if size change    */
  924.     if (growRes = GrowWindow (thePort, startPt, &r)) {
  925.         SizeWindow (thePort, LoWord (growRes), HiWord (growRes), false);
  926.         TriggerUpdate (h, thePort, mods);
  927.     }
  928. }
  929.  
  930.  
  931. /*
  932.     Zoom the current window.  Very similar to DoGrow
  933.  
  934.     Since the zoombox of only the active window may be clicked,
  935.     it should not be necessary to set the port.
  936. */
  937.  
  938. void DoZoom (WHandler **, GrafPtr, short, short);
  939. static void DoZoom (h, zoomPort, partCode, mods)
  940.     register WHandler    **h;
  941.     GrafPtr                zoomPort;
  942.     short                partCode;
  943.     short                mods;
  944. {
  945.     if (pZoomProc) {
  946.         /* for a custom zoom proc, allow the user to modify the behaviour */
  947.         (*pZoomProc) (zoomPort, partCode, mods, false);
  948.     }
  949.     else {
  950.         ZoomWindow (zoomPort, partCode, false);
  951.     }
  952.     TriggerUpdate (h, zoomPort, mods);
  953. }
  954.  
  955. /*
  956.     General event handler
  957. */
  958.  
  959. void DoEvent (EventRecord *);
  960. static void DoEvent (theEvt)
  961.     EventRecord    *theEvt;
  962. {
  963.     register EventRecord    *theEvent;
  964.     Point                    evtPt;
  965.     GrafPtr                    evtPort;
  966.     register short            evtPart;
  967.     register char            evtChar;
  968.     register unsigned char    evtCode;
  969.     register short            evtMods = 0;
  970.     register WHandler        **h, **t;
  971.     Rect                    r;
  972.     WindowPtr                frontWindow = FrontWindow ();
  973.  
  974.     theEvent = theEvt;
  975.  
  976. #ifdef    supportDialogs
  977.     if(DoDialog (theEvent))
  978.         return;
  979. #endif
  980.  
  981.     evtPt = theEvent->where;
  982.     evtMods = theEvent->modifiers;
  983.  
  984.     switch (theEvent->what) {
  985.         case app4Evt:        /* multifinder event */
  986.             {
  987.                 short flag = ((theEvent->message >> 24) & 0xff);
  988.                 if (flag == 0x01) {
  989.                     fgApp = ((theEvent->message & 0x01) != 0);
  990.                     DoActivate (frontWindow, fgApp);
  991.                     /* does the app want to know about suspend/resume? */
  992.                     if (pSuspendResume) {
  993.                         (*pSuspendResume)(fgApp);
  994.                     }
  995.                     /* should we convert the scrap?  Always convert if suspend
  996.                        but only convert on resume if "changed scrap" flag bit is set */
  997.                     if ((!fgApp || (theEvent->message & 0x02)) && pScrapConvert) {
  998.                         (*pScrapConvert)(fgApp);
  999.                     }
  1000.                 }
  1001.                 else if (flag == 0xfa && fgApp) {
  1002.                     SkelCheckCursor (GetWDHandler (frontWindow), evtPt, cursorRgn);
  1003.                 }
  1004.                 else if (flag == 0xfd) {
  1005.                     if (pChildDied) {
  1006.                         (*pChildDied) (theEvent);
  1007.                     }
  1008.                 }
  1009.                 else if (pHandleApp4) {
  1010.                     (*pHandleApp4) (theEvent);
  1011.                 }
  1012.             }
  1013.             /* fall through to do "idle" processing */
  1014.         case nullEvent:
  1015.             if (pBkgnd != nil) {
  1016.                 (*pBkgnd) ();
  1017.             }
  1018.             if (!useWaitNextEvent && !PtInRgn (evtPt, cursorRgn)) {
  1019.                 SkelCheckCursor (GetWDHandler (frontWindow), theEvent->where, cursorRgn);
  1020.             }
  1021.             break;
  1022. /*
  1023.     Mouse click.  Get the window that the click occurred in, and the
  1024.     part of the window.
  1025. */
  1026.         case mouseDown:
  1027.         {
  1028.             evtPart = FindWindow (evtPt, &evtPort);
  1029.             h = GetWHandler (evtPort);
  1030. #ifdef supportDialogs
  1031.             /* don't allow mouse clicks outside a modal dialog */
  1032.             if (evtPort != frontWindow &&
  1033.                     (t = GetWDHandler(FrontWindow())) != nil &&
  1034.                     (**t).whModal) {
  1035.                 break;
  1036.             }
  1037. #endif
  1038.             switch (evtPart)
  1039.             {
  1040. /*
  1041.     Click in a desk accessory window.  Pass back to the system.
  1042. */
  1043.                 case inSysWindow:
  1044.                 {
  1045.                     SystemClick (theEvent, evtPort);
  1046.                     break;
  1047.                 }
  1048. /*
  1049.     Click in menu bar.  Track the mouse and execute selected command,
  1050.     if any.
  1051. */
  1052.                 case inMenuBar:
  1053.                 {
  1054.                     DoMenuCommand (MenuSelect (evtPt));
  1055.                     break;
  1056.                 }
  1057. /*
  1058.     Click in grow box.  Resize window.
  1059. */
  1060.                 case inGrow:
  1061.                 {
  1062.                     if (h) {
  1063.                         if ((**h).whCanGrow) {
  1064.                             DoGrow (h, evtPort, evtPt, evtMods);
  1065.                         }
  1066.                         else {
  1067.                             DoMouse (h, theEvent);
  1068.                         }
  1069.                     }
  1070.                     break;
  1071.                 }
  1072. /*
  1073.     Click in title bar.  Drag the window around.  Leave at least
  1074.     4 pixels visible in both directions.
  1075.     Bug fix:  The window is selected first to make sure it's at least
  1076.     activated (unless the command key is down-see Inside Macintosh).
  1077.     DragWindow seems to call StillDown first, so that clicks in drag
  1078.     regions while machine is busy don't otherwise bring window to front if
  1079.     the mouse is already up by the time DragWindow is called.
  1080. */
  1081.                 case inDrag:
  1082.                 {
  1083.                     r = screenPort->portRect;
  1084.                     r.top += mBarHeight;            /* skip down past menu bar */
  1085.                     InsetRect (&r, 4, 4);
  1086.                     SkelDragWindow (evtPort, evtPt, evtMods, &r);
  1087.                     break;
  1088.                 }
  1089. /*
  1090.     Click in close box.  Call the close proc if the window has one.
  1091. */
  1092.                 case inGoAway:
  1093.                 {
  1094.                     if (TrackGoAway (evtPort, evtPt))
  1095.                         DoClose (h);
  1096.                     break;
  1097.                 }
  1098. /*
  1099.     Click in content region.  If the window wasn't frontmost (active),
  1100.     just select it, otherwise pass the click to the window's mouse
  1101.     click handler.
  1102. */
  1103.                 case inContent:
  1104.                 {
  1105.                     if (evtPort != frontWindow) {
  1106.                         SelectWindow (evtPort);
  1107.                         SetPort (evtPort);
  1108.                     }
  1109.                     else
  1110.                         DoMouse (h, theEvent);
  1111.                     break;
  1112.                 }
  1113.  
  1114. /*
  1115.     Click in zoom box.  Track the click and then zoom the window if
  1116.     necessary
  1117. */
  1118.                 case inZoomIn:
  1119.                 case inZoomOut:
  1120.                 {
  1121.                     if(TrackBox(evtPort, evtPt, evtPart))
  1122.                         DoZoom (h, evtPort, evtPart, evtMods);
  1123.                     break;
  1124.                 }
  1125.  
  1126.             }
  1127.             break;    /* mouseDown */
  1128.         }
  1129. /*
  1130.     Key event.  If the command key was down, process as menu item
  1131.     selection, otherwise pass the character and the modifiers flags
  1132.     to the active window's key handler.
  1133.  
  1134.     If dialogs are supported, there's no check for command-key
  1135.     equivalents, since that would have been checked in DoDialog.
  1136. */
  1137.         case keyDown:
  1138.         case autoKey:
  1139.         {
  1140.             evtCode = (theEvent->message & keyCodeMask) >> 8;
  1141.             evtChar = theEvent->message & charCodeMask;
  1142. #ifndef supportDialogs
  1143.             if (evtMods & cmdKey) {
  1144.                 /* try menu equivalent */
  1145.                 DoMenuCommand (MenuKey (evtChar));
  1146.                 break;
  1147.             }
  1148. #endif
  1149.             /* frontWindow is at least frontDocWindow--
  1150.                 tools can't get keys! (Yet!!) */
  1151.             DoKey (GetWHandler (frontWindow), evtChar, evtCode, evtMods);
  1152.             break;
  1153.         }
  1154.  
  1155.         case keyUp:
  1156.         {
  1157.             evtCode = 0x80 | ( (theEvent->message & keyCodeMask) >> 8 );
  1158.             evtChar = theEvent->message & charCodeMask;
  1159.  
  1160.             DoKey (GetWHandler (frontWindow), evtChar, evtCode, evtMods);
  1161.             break;
  1162.         }
  1163.         
  1164. /*
  1165.     Update a window.
  1166. */
  1167.         case updateEvt:
  1168.         {
  1169.             DoUpdate (GetWDHandler ((WindowPtr) theEvent->message));
  1170.             break;
  1171.         }
  1172. /*
  1173.     Activate or deactivate a window.
  1174. */
  1175.         case activateEvt:
  1176.         {
  1177.             DoActivate ((WindowPtr) theEvent->message,
  1178.                         ((theEvent->modifiers & activeFlag) != 0));
  1179.             break;
  1180.         }
  1181. /*
  1182.     handle inserts of uninitialized disks
  1183. */
  1184.         case diskEvt:
  1185.         {
  1186.             if (HiWord (theEvent->message) != noErr) {
  1187.                 DILoad ();
  1188.                 (void) DIBadMount (diskInitPt, theEvent->message);
  1189.                 DIUnload ();
  1190.             }
  1191.             break;
  1192.         }
  1193. /*
  1194.     handle network events
  1195. */
  1196.         case networkEvt:
  1197.         {
  1198.             if (pNetworkProc) {
  1199.                 (*pNetworkProc) (theEvent);
  1200.             }
  1201.             break;
  1202.         }
  1203.     }
  1204. }
  1205.  
  1206.  
  1207. /* -------------------------------------------------------------------- */
  1208. /*                        Interface (public) Routines                        */
  1209. /* -------------------------------------------------------------------- */
  1210.  
  1211.  
  1212. /*
  1213.     Initialize the various Macintosh Managers.
  1214.     Set default upper limits on window sizing.
  1215.     FlushEvents does NOT toss disk insert events, so that disks
  1216.     inserted while the application is starting up don't result
  1217.     in dead drives.
  1218.     Determine whether to use WaitNextEvent or not.
  1219.     Set up big rectangle for cursor management.
  1220. */
  1221. #define _WaitNextEvent 0xA860
  1222.  
  1223. void SkelInit (resume, extra)
  1224.     void (*resume)();
  1225.     int extra;
  1226. {
  1227.     Rect r;
  1228.  
  1229.     MaxApplZone ();
  1230.     FlushEvents (everyEvent - diskMask, 0 );
  1231. #ifdef applec
  1232.     InitGraf (&qd.thePort);
  1233. #else
  1234.     InitGraf (&thePort);
  1235. #endif
  1236.     InitFonts ();
  1237.     InitWindows ();
  1238.     InitMenus ();
  1239.     TEInit ();
  1240. #ifdef applec
  1241.     InitDialogs ((ResumeProcPtr) resume);        /* no restart proc */
  1242. #else
  1243.     InitDialogs ((ProcPtr) resume);        /* no restart proc */
  1244. #endif
  1245.     InitCursor ();
  1246.  
  1247.     while (extra-- > 0)
  1248.         MoreMasters ();
  1249.  
  1250. /*
  1251.     Set upper limits of window sizing to machine screen size.  Allow
  1252.     for the menu bar (use the glue from the Script Manager).
  1253. */
  1254.     GetWMgrPort (&screenPort);
  1255.     growRect.top = 80;
  1256.     growRect.left = 80;
  1257.     growRect.right = screenPort->portRect.right;
  1258.     growRect.bottom = screenPort->portRect.bottom - (mBarHeight = GetMBarHeight ());
  1259.  
  1260.     /* this is (at this time anyway) pointless */
  1261.     SkelEventMask (eventMask);
  1262.  
  1263.     /* throw away any error return */
  1264.     (void) SysEnvirons (curSysEnvVers, &skelEnvRec);
  1265.     useWaitNextEvent = SkelCheckTrap (_WaitNextEvent, ToolTrap);
  1266.  
  1267.     /* build a region that covers all of the displays available */
  1268.     screensRgn = NewRgn ();
  1269.     SetRect (&r, -32768, -32768, 32767, 32767);
  1270.     /* account for bug in 128K ROMs */
  1271.     if (skelEnvRec.machineType < envMacII)
  1272.         InsetRect (&r, 1, 1);
  1273.     RectRgn (screensRgn, &r);
  1274.     /* a place to watch the cursor in */
  1275.     cursorRgn = NewRgn ();
  1276. }
  1277.  
  1278. /*
  1279.     Main loop.
  1280.  
  1281.     Take care of DA's with SystemTask (not needed with WaitNextEvent).
  1282.     Run background task if there is one. (now at nullEvents in DoEvent)
  1283.     If there is an event, check for an event hook.  If there isn't
  1284.     one defined, or if there is but it returns false, call the
  1285.     general event handler.  (Hook returns true if TransSkel should
  1286.     ignore the event.)
  1287.     If no event, call the "no-event" handler for the front window and for
  1288.     any other windows with idle procedures that are always supposed
  1289.     to run.  This is done in such a way that it is safe for idle procs
  1290.     to remove the handler for their own window if they want (unlikely,
  1291.     but...)  This loop doesn't check whether the window is really
  1292.     a dialog window or not, but it doesn't have to, because such
  1293.     things always have a nil idle proc.
  1294.     
  1295.     doneFlag is reset upon exit.  This allows it to be called
  1296.     repeatedly, or recursively.
  1297.  
  1298.     If dialogs are supported, null events are looked at (in SkelMain)
  1299.     and passed to the event handler.  This is necessary to make sure
  1300.     DialogSelect gets called repeatedly, or the caret won't blink if
  1301.     a dialog has any editText items.
  1302.  
  1303.     In order to be more multi-finder compatible, null-events are passed
  1304.     through to doEvent--where the appropriate idle processing is done.
  1305. */
  1306.  
  1307.   Boolean
  1308. SkelWaitNextEvent (evtMask, evt, timeOut, checkRgn)
  1309.   short evtMask;
  1310.   EventRecord *evt;
  1311.   long timeOut;
  1312.   RgnHandle checkRgn;
  1313. {
  1314.     if (useWaitNextEvent) {
  1315.         return (WaitNextEvent (evtMask, evt, timeOut, checkRgn));
  1316.     }
  1317.     else {
  1318.         SystemTask ();
  1319.         return (GetNextEvent (evtMask, evt));
  1320.     }
  1321. }
  1322.  
  1323. /*
  1324.     if checkRgn is nil then mouse-moved events *won't* be generated
  1325. */
  1326. void SkelOnePass (checkRgn)
  1327.     RgnHandle checkRgn;
  1328. {
  1329.     EventRecord            theEvent;
  1330.     register WHandler    **wh, **wh2;
  1331.     Boolean                haveEvent;
  1332.     WindowPtr            frontWindow;
  1333.     long                timeOut = pBGTime;
  1334.  
  1335. /*
  1336.     Now watch carefully.  GetNextEvent calls SystemEvent to handle some
  1337.     DA events, and returns false if the event was handled.  However, in
  1338.     such cases the event record will still have the event that occurred,
  1339.     *not* a null event, as you might reasonably expect.  So it's not
  1340.     enough to look at haveEvent.
  1341.  
  1342.     Previous versions figured (wrongly) that haveEvent==false meant a null
  1343.     event had occurred, and passed it through to DoEvent and DoDialog, so
  1344.     that caret-blinking in dialog TextEdit items would occur.  But cmd-key
  1345.     equivalents while DA windows were in front, in particular, allowed
  1346.     already-processed DA events to get into DoEvent (because haveEvent
  1347.     was false), and they got handled twice because when the event record
  1348.     was examined, lo and behold, it had a cmd-key event!  So now this
  1349.     logic is used:
  1350.  
  1351.     If have a real event, and there's no event hook or there is but it
  1352.     doesn't handle the event, OR if the "non-event" is a true nullEvent,
  1353.     then process it. (modified to allow nullEvents to go to event hook)
  1354. */
  1355.         
  1356.     if (fgApp) {
  1357.         timeOut = pFGTime;
  1358.     }
  1359.     haveEvent = SkelWaitNextEvent (eventMask, &theEvent, timeOut, checkRgn);
  1360.  
  1361.     /* ignore haveEvent here so that event hook will get null events
  1362.        a check for a nullEvent is pretty silly in this case */
  1363.     if ((pEvent == nil || (*pEvent)(&theEvent) == false))
  1364.         DoEvent(&theEvent);
  1365.  
  1366. /*
  1367.     Run applicable idle procs.  Make sure to save and restore the port,
  1368.     since idle procs for the non-active window may be run.
  1369. */
  1370.     if (!haveEvent || theEvent.what == nullEvent) {
  1371.         frontWindow = FrontWindow ();
  1372.         for (wh = whList; wh != nil; wh = wh2) {
  1373.             wh2 = (**wh).whNext;
  1374.             if ((**wh).whWind == frontWindow || !(**wh).whFrontOnly) {
  1375.                 DoIdle (wh);
  1376.             }
  1377.         }
  1378.     }
  1379. }
  1380.  
  1381. void SkelMain ()
  1382. {
  1383. #ifdef    CHECK_STACK
  1384.     long stackFree;
  1385. #endif
  1386.     /* create a region to do cursor handling in */
  1387.     while (!doneFlag) {    
  1388.         SkelOnePass (cursorRgn);
  1389. #ifdef    CHECK_STACK
  1390.         stackFree = StackSpace ();
  1391. #endif
  1392.     }
  1393.     doneFlag = false;
  1394.     /* ditch the cursor control region */
  1395. }
  1396.  
  1397.  
  1398. /*
  1399.     Tell SkelMain to stop
  1400. */
  1401.  
  1402. void SkelWhoa ()
  1403. {
  1404.     doneFlag = true;
  1405. }
  1406.  
  1407.  
  1408. /*
  1409.     Clobber all the menu, window and dialog handlers
  1410. */
  1411.  
  1412. void SkelClobber ()
  1413. {
  1414.     while (whList != nil)
  1415.         SkelRmveWind ((**whList).whWind);
  1416.  
  1417.     /* 'false' removes the annoying flicker as menus are deleted */
  1418.     while (mhList != nil)
  1419.         RemoveMenu (GetMHandle((**mhList).mhID), false);
  1420.     /* draw the menu bar (now empty) in case there's some system level cleanup */
  1421.     DrawMenuBar ();
  1422.     DisposeRgn (cursorRgn);
  1423.     /* make the event mask innocuous for uniFinder */
  1424.     SetEventMask (everyEvent ^ keyUpMask);
  1425. }
  1426.  
  1427.  
  1428. /* -------------------------------------------------------------------- */
  1429. /*                        Menu-handler interface routines                    */
  1430. /* -------------------------------------------------------------------- */
  1431.  
  1432.  
  1433. /*
  1434.     Install handler for a menu.  Remove any previous handler for it.
  1435.     Pass the following parameters:
  1436.  
  1437.     theMenu    Handle to the menu to be handled.  Must be created by host.
  1438.     pSelect    Proc that handles selection of items from menu.  If this is
  1439.             nil, the menu is installed, but nothing happens when items
  1440.             are selected from it.
  1441.     pClobber Proc for disposal of handler's data structures.  Usually
  1442.             nil for menus that remain in menu bar until program
  1443.             termination.
  1444.     isPull    Should this be installed in the menubar?
  1445.     drawBar    draw the menu bar?
  1446. */
  1447.  
  1448. void SkelMenu (theMenu, pSelect, pClobber, isPull, drawBar)
  1449.     MenuHandle    theMenu;
  1450. #ifdef applec
  1451.     void        (*pSelect)(short);
  1452.     void        (*pClobber)(MenuHandle);
  1453. #else
  1454.     void        (*pSelect)();
  1455.     void        (*pClobber)();
  1456. #endif
  1457.     Boolean        isPull;
  1458.     Boolean        drawBar;
  1459. {
  1460.     register MHandler    **mh;
  1461.  
  1462.     mhClobOnRmve = false;
  1463.     SkelRmveMenu (theMenu);
  1464.     mhClobOnRmve = true;
  1465.  
  1466.     mh = New (MHandler);
  1467.     (**mh).mhNext = mhList;
  1468.     mhList = mh;
  1469.     (**mh).mhID = (**theMenu).menuID;            /* get menu id number */
  1470.     (**mh).mhSelect = pSelect;                    /* install selection handler */
  1471.     (**mh).mhClobber = pClobber;                /* install disposal handler */
  1472.     InsertMenu (theMenu, (isPull) ? -1 : 0);    /* insert menu in menulist */
  1473.     if (drawBar)
  1474.         DrawMenuBar ();
  1475. }
  1476.  
  1477. /*
  1478.     Remove a menu from the menu list for the user
  1479. */
  1480.  
  1481. void SkelRmveMenu (theMenu)
  1482.     MenuHandle    theMenu;
  1483. {
  1484.     /* 'true' forces the menubar to be redrawn */
  1485.     RemoveMenu (theMenu, true);
  1486. }
  1487.  
  1488. /*
  1489.     Install a handler for the Apple menu.
  1490.     
  1491.     SkelApple is called if TransSkel is supposed to handle the apple
  1492.     menu itself.  aboutTitle is the title of the first item.  If nil,
  1493.     then only desk accessories are put into the menu.  If not nil, then
  1494.     the title is entered as the first item, followed by a gray line,
  1495.     then the desk accessories.
  1496.  
  1497.     SkelApple does not cause the menubar to be drawn, so if the Apple
  1498.     menu is the only menu, DrawMenuBar must be called afterward.
  1499.  
  1500.     No value is returned, unlike SkelMenu.  It is assumed that SkelApple
  1501.     will be called so early in the application that the call the SkelMenu
  1502.     is virtually certain to succeed.  If it doesn't, there's probably
  1503.     little hope for the application anyway.
  1504. */
  1505.  
  1506. void SkelApple (aboutTitle, aboutProc)
  1507.     char        *aboutTitle;
  1508. #ifdef applec
  1509.     void        (*aboutProc)(void);
  1510. #else
  1511.     void        (*aboutProc)();
  1512. #endif
  1513. {
  1514.     appleID = 1;
  1515.     appleMenu = NewMenu (appleID, "\p\024");    /* 024 = apple character */
  1516.     if (aboutTitle != nil) {
  1517.         doAbout = true;
  1518.         AppendMenu (appleMenu, aboutTitle);    /* add About... item title */
  1519.         AppendMenu (appleMenu, "\p(-");        /* add gray line */
  1520.         appleAboutProc = aboutProc;
  1521.     }
  1522.     AddResMenu (appleMenu, 'DRVR');        /* add desk accessories */
  1523.     SkelMenu (appleMenu, DoAppleItem, DoAppleClobber, false, false);
  1524. }
  1525.  
  1526. /*
  1527.     Remove a window handler.  This calls the handler's disposal routine
  1528.     and then takes the handler out of the handler list and disposes
  1529.     of it.
  1530.  
  1531.     SkelRmveWind is also called by SkelRmveDlog.
  1532. */
  1533.  
  1534. void SkelRmveWind (theWind)
  1535.     WindowPtr    theWind;
  1536. {
  1537. register WHandler    **h, **h2;
  1538.  
  1539.     if (whList != nil) {
  1540.         /* if list empty, ignore */
  1541.         if ((**whList).whWind == theWind) {
  1542.             /* is it the first element? */
  1543.             h2 = whList;
  1544.             whList = (**whList).whNext;
  1545.         }
  1546.         else {
  1547.             for (h = whList; h != nil; h = h2) {
  1548.                 h2 = (**h).whNext;
  1549.                 if (h2 == nil) {
  1550.                     /* theWind not in list! */
  1551.                     return;
  1552.                 }
  1553.                 if ((**h2).whWind == theWind) {
  1554.                     /* found it */
  1555.                     (**h).whNext = (**h2).whNext;
  1556.                     break;
  1557.                 }
  1558.             }
  1559.         }
  1560.         if (whClobOnRmve)
  1561.             DoClobber (h2);                /* call disposal routine */
  1562.         if ((**h2).whCursorRgn) {
  1563.             DisposeRgn ((**h2).whCursorRgn);
  1564.         }
  1565.         DisposHandle ((Handle) h2);        /* get rid of handler record */
  1566.     }
  1567. }
  1568.  
  1569. /*
  1570.     Set a cursor and a region to allow WaitNextEvent to return mouse-moved
  1571.     events.  This routine should probably also allow a routine to be passed
  1572.     in that would be called when the cursor changed—allowing multiple regions
  1573.     (and multiple cursors) per window, or perhaps a list of regions and
  1574.     cursors.  At the moment the most economical and useful way of doing this
  1575.     is unclear.  -RTS
  1576. */
  1577.  
  1578. /* -------------------------------------------------------------------- */
  1579. /*                    Miscellaneous interface routines                    */
  1580. /* -------------------------------------------------------------------- */
  1581.  
  1582. /*
  1583.     Override the default sizing limits for a window, or, if theWind
  1584.     is nil, reset the default limits used by SkelWindow.
  1585. */
  1586. /*
  1587.     Set the event mask.  Allow keyup events. -DTH
  1588. */
  1589.  
  1590. void SkelEventMask (mask)
  1591.     short    mask;
  1592. {
  1593.     eventMask = mask;
  1594.     if (everyEvent == (eventMask | keyUpMask))
  1595.         SetEventMask (eventMask);
  1596. }
  1597.  
  1598.  
  1599. /*
  1600.     Check for the existence of a trap
  1601. */
  1602. #define _Unimplemented                  0xA89F
  1603.  
  1604.   Boolean
  1605. SkelCheckTrap (trapNumber, trapKind)
  1606.     short trapNumber;
  1607. #ifdef applec
  1608.     TrapType trapKind;
  1609. #else
  1610.     short trapKind;
  1611. #endif
  1612. {
  1613.     if (trapKind == ToolTrap && (skelEnvRec.machineType > envMachUnknown &&
  1614.             skelEnvRec.machineType < envMacII)) {
  1615.         trapNumber &= 0x03FF;
  1616.         if (trapNumber > 0x01FF) {
  1617.             /* ToolTraps don't exist on these machines */
  1618.             trapNumber = _Unimplemented;
  1619.         }
  1620.     }
  1621.     return (NGetTrapAddress (trapNumber, trapKind) !=
  1622.                             GetTrapAddress (_Unimplemented));
  1623. }
  1624.  
  1625.